//
//@author jltxseo
// Created by junlintianxia on 2019/3/31.
//
#include <jni.h>
#include <stdio.h>
#include <time.h>
#include "FFmpegCmd.h"
#include "XqLog.h"
#include "ffmpegMod.h"

/**
 * 对ffmpeg的头文件的引用需要使用extern "C"{include "}
 */
extern "C" {
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libavfilter/avfilter.h"
#include "libavutil/log.h"
#include "ffmpeg.h"
}

static JavaVM *jvm = NULL;//java虚拟机

void custom_log(void *ptr, int level, const char *fmt, va_list vl) {
    FILE *fp = fopen(
            "/storage/emulated/0/Android/data/com.xq.jltx.ffmpegsinglecmd/files/av_log.txt", "a+");
    if (fp) {
        vfprintf(fp, fmt, vl);
        fflush(fp);
        fclose(fp);
    }
}

/**
 * 在加载动态库时回去调用 JNI_Onload 方法，在这里可以得到 JavaVM 实例对象
 * @param vm
 * @param reserved
 * @return
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    //获取java虚拟机，在jni的c线程中不允许使用共用的env环境变量 但JavaVM在整个jvm中是共用的 可通过保存JavaVM指针,到时候再通过JavaVM指针取出JNIEnv *env
    jvm = vm;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}
/*
 * Class:     com_xq_jltx_ffmpeg_FFmpegCmd
 * Method:    exec
 * Signature: (I[Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_xq_jltx_ffmpeg_FFmpegCmd_exec
        (JNIEnv *env, jclass jzclass, jint cmdnum, jobjectArray cmdline) {
    av_log_set_callback(custom_log);
    int argc = cmdnum;
    char **argv = (char **) malloc(sizeof(char *) * argc);

    int i = 0;
    for (i = 0; i < argc; i++) {
        jstring string = static_cast<jstring>(env->GetObjectArrayElement(cmdline, i));
        const char *tmp = env->GetStringUTFChars(string, 0);
        argv[i] = (char *) malloc(sizeof(char) * 1024);
        strcpy(argv[i], tmp);
    }
    LOGE("exec start");
    exec(argc, argv);
    LOGE("exec end");
    for (i = 0; i < argc; i++) {
        free(argv[i]);
    }
    free(argv);
    return 0;
}


void callJavaProgressMethod(char *ret, long fileDuration) {
    long result = 0;
    char timeStr[10] = "time=";
    char fameStr[10] = "frame=";
    //查找time=索引位置，判断字符串字符存在的索引
    char *q = strstr(ret, timeStr);
    //查找frame=索引位置，判断字符串字符存在的索引
    char *frame = strstr(ret, fameStr);
    if (q != NULL && frame != NULL && fileDuration > 0L) {
        char str[17] = {0};
        strncpy(str, q, 16);
        LOGI("str=%s", str);
        long h = (str[5] - '0') * 10 + (str[6] - '0');
        long m = (str[8] - '0') * 10 + (str[9] - '0');
        long s = (str[11] - '0') * 10 + (str[12] - '0');
        long ms = (str[14] - '0') * 10 + (str[15] - '0');
        result = (s + m * 60 + h * 60 * 60) * 1000 + ms;
        JNIEnv *env;
        int  attach;
        env = JNI_GetEnv(&attach);

        //1 . 找到java代码的 class文件
        //    jclass      (*FindClass)(JNIEnv*, const char*);
        jclass dpclazz = env->FindClass("com/xq/jltx/ffmpeg/FFmpegCmd");
        if (dpclazz == 0) {
            LOGI("find class error");
            return;
        }
        LOGI("find class ");

        //2 寻找class里面的方法
        //   jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
        jmethodID method1 = env->GetStaticMethodID(dpclazz, "onExecProgress", "(JJ)V");
        if (method1 == 0) {
            LOGI("find onExecProgress error");
            return;
        }
        LOGI("find onExecProgress ");
        //3 .调用这个方法
        //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
        long duration = fileDuration / 1000L;
        LOGE("result=%ld,duration=%ld", result,duration);
        if(result < 0L){
            return;
        }
        env->CallStaticVoidMethod(dpclazz, method1, (jlong) result,(jlong)duration);
    }
}

JNIEnv * JNI_GetEnv(int *attach) {
    if (jvm == NULL)
        return NULL;

    int status;

    //判断是否有AttachCurrentThread
    *attach = 0;

    JNIEnv *_jniEnv = NULL;

    status = jvm->GetEnv((void **) &_jniEnv, JNI_VERSION_1_6);

    if (status == JNI_EDETACHED || _jniEnv == NULL) {
        status = jvm->AttachCurrentThread(&_jniEnv, NULL);
        if (status < 0) {
            _jniEnv = NULL;
        } else {
            *attach = 1;
        }
    }
    return _jniEnv;
}

int JNI_DelEnv() {
    if (jvm == NULL)
        return -1;

    return jvm->DetachCurrentThread();
}







